/* subr.c */

/*
 * Supporting subroutines for the menu generation 
 * language (MGL)
 *
 * Tony Mason
 * November 1988
 * Completed by John Levine, August 1992
 */

/* includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mglyac.h"
#include "mgl-code"              /* contains definitions of 
				  * skeleton file to be built */

extern FILE *yyin, *yyout;

/* imports */
extern int screen_done;
extern char *cmd_str, *act_str,*item_str;

/* exports */

/* local */
static char current_screen[100]; /* reasonable? */
static int done_start_init;
static int done_end_init;
static int current_line;
struct item {
	char           *desc;         /* item description */
	char           *cmd;          /* command */
	int            action;        /* action to take */
	char           *act_str;      /* action operation */
	int            attribute;     /* visible/invisible */
	struct item    *next;         /* next member of list */
} *item_list, *last_item;

/* macros */
#define SCREEN_SIZE 80

void cfree(char *);	/* free if not null */

/* code */

/*
 * start_screen:
 * This routine begins preparation of the screen.  It 
 * writes the preamble and modifies the global state 
 * variable screen_done to show that a screen is in 
 * progress (thus, if a screen is in progress when EOF 
 * is seen, an appropriate error message can be given).
 */

start_screen(char *name)  /* name of screen to create */
{
	long time(),tm = time((long *)0);
	char *ctime();

	if(!done_start_init)
	{
		fprintf(yyout, "/*\n * Generated by MGL: %s */\n\n",
		    ctime(&tm));
		dump_data(screen_init);
		done_start_init = 1;
	}
	if(check_name(name) == 0)
		warning("Reuse of name",name);
	fprintf(yyout, "/* screen %s */\n", name);
	fprintf(yyout, "menu_%s()\n{\n",name);
	fprintf(yyout, "\textern struct item menu_%s_items[];\n\n",name);
	fprintf(yyout, "\tif(!init) menu_init();\n\n");
	fprintf(yyout, "\tclear();\n\trefresh();\n");

	if(strlen(name) > sizeof current_screen)
		warning("Screen name is larger than buffer",(char *)0);
	strncpy(current_screen, name, sizeof(current_screen) - 1);

	screen_done = 0;
	current_line = 0;

	return 0;
}

/*
 * add_title:
 * Add centered text to screen code.
 */
add_title(line)
char *line;
{
	int length = strlen(line);
	int space = (SCREEN_SIZE - length) / 2;

	fprintf(yyout, "\tmove(%d,%d);\n",current_line, space);
	current_line++;
	fprintf(yyout, "\taddstr(\"%s\");\n",line);
	fprintf(yyout, "\trefresh();\n");
}

/*
 * add_line:
 * Add a line to the actions table.  It will be written 
 * out after all lines have been added.  Note that some 
 * of the information is in global variables.
 */

add_line(action, attrib)
int action, attrib;
{
	struct item *new;

	new = (struct item *)malloc(sizeof(struct item));

	if(!item_list)
	{	/* first item */
		item_list = last_item = new;
	}
	else
	{	/* already items on the list */
		last_item->next = new;
		last_item = new;
	}

	new->next = NULL;  /* mark end of list */

	new->desc = item_str;
	new->cmd = cmd_str;
	new->action = action;

	switch(action)
	{
	case EXECUTE:
		new->act_str = act_str;
		break;
	case MENU:
		new->act_str = act_str;
		break;
	default:
		new->act_str = 0;
		break;
	}
	new->attribute = attrib;

}

/*
 * end_screen:
 * Finish screen, print out postamble.
 */

end_screen(char *name)
{

	fprintf(yyout, "\tmenu_runtime(menu_%s_items);\n",name);

	if(strcmp(current_screen,name) != 0)
	{
		warning("name mismatch at end of screen", 
		    current_screen);
	}
	fprintf(yyout, "}\n");
	fprintf(yyout, "/* end %s */\n",current_screen);

	process_items();

	/* write initialization code out to file */
	if(!done_end_init)
	{
		done_end_init = 1;
		dump_data(menu_init);
	}

	current_screen[0] = '\0';	/* no current screen */

	screen_done = 1;

	return 0;
}

/*
 * process_items:
 * Walk the list of menu items and write them to an 
 * external initialized array.  Also defines the symbolic 
 * constant used for the run-time support module (which 
 * is below this table).
 */
process_items()
{
	int cnt = 0;
	struct item *ptr;

	if(item_list == 0)
		return; /* nothing to do */
	fprintf(yyout, "struct item menu_%s_items[]={\n",current_screen);
	ptr = item_list;

	/* climb through the list */
	while(ptr)
	{
		struct item *optr;

		if(ptr->action == MENU)
			fprintf(yyout, "{\"%s\",\"%s\",%d,\"\",%s,%d},\n",
			    ptr->desc,ptr->cmd, ptr->action,
			    ptr->act_str,ptr->attribute);
		else
			fprintf(yyout, "{\"%s\",\"%s\",%d,\"%s\",0,%d},\n",
			    ptr->desc,ptr->cmd, ptr->action,
			    ptr->act_str ? ptr->act_str : "",
			    ptr->attribute);

		cfree(ptr->desc);
		cfree(ptr->cmd);
		cfree(ptr->act_str);
		optr = ptr;
		ptr = ptr->next;
		free(optr);
		cnt++;
	}
	fprintf(yyout, "{(char *)0, (char *)0, 0, (char *)0, 0, 0},\n");
	fprintf(yyout, "};\n\n");
	item_list = 0;

	/* next the run-time module that does all the "work" */;
}

/*
 * This routine takes a null-terminated list of strings 
 * and prints them on the standard out.  Its sole purpose 
 * in life is to dump the big static arrays making up the 
 * runtime code for the menus generated.
 */

dump_data(array)
char **array;
{
	while(*array)
		fprintf(yyout, "%s\n",*array++);
}


/*
 * this routine writes out the run-time support
 */

end_file()
{

	dump_data(menu_runtime);
}

/*
 * Check a name to see if it has already been used.  If 
 * not, return 1; otherwise, return 0.  This routine also 
 * squirrels away the name for future reference.  Note 
 * that this routine is purely dynamic.  It would be 
 * easier to just set up a static array, but less flexible.
 */

check_name(name)
char *name;
{
	static char **names = 0;
	static name_count = 0;
	char **ptr,*newstr;

	if(!names)
	{
		names = (char **)malloc(sizeof(char *));
		*names = 0;
	}

	ptr = names;
	while(*ptr)
	{
		if(strcmp(name,*ptr++) == 0) return 0;
	}

	/* not in use */
	name_count++;
	names = (char **)realloc(names, (name_count+1) * sizeof(char *));
	names[name_count] = 0;
	newstr = strdup(name);
	names[name_count-1] = newstr;
	return 1;
}

void
cfree(char *p)
{
	if(p)
		free(p);
}
